#include <errno.h>
#include <stdnoreturn.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/times.h>
#include <unistd.h>
#include "uart_putchar.h"

#undef errno
extern int  errno;

int _isatty(int file)
{
	if ((file == STDOUT_FILENO) || (file == STDERR_FILENO) || (file == STDIN_FILENO)){
		return 1;
	}
	else{
		errno = EBADF;
		return -1;
	}
}

ssize_t _write(int file, const void *ptr, size_t len)
{
	const uint8_t * current = (const char *)ptr;
	size_t i;

	if (_isatty(file)==1) {
		if (UART_BASE == 0){
			for (i = 0; i < len; i++) {
				if (current[i] == '\n') simu_putchar('\r');
				simu_putchar(current[i]);
			}
			return len;
		}
		for (i = 0; i < len; i++) {
			if (current[i] == '\n') uart_putchar('\r');
			uart_putchar(current[i]);
		}
    	return len;
	}
  	errno = EBADF;
  	return -1;
}

ssize_t _read(int file, void* ptr, size_t len)
{
	size_t  i;
	char* ptr_f = (char*)ptr;
	if (file == STDIN_FILENO){
		if(UART_BASE == 0){
			return 0;
		}
		for (i = 0; i < len; i++){
	  		ptr_f[i] = uart_getchar();
			/* Return partial buffer if we get EOL */
	  		if ('\n' == ptr_f[i])
	    	{
	      		return  i;
	    	}
		}
        return  i;
    }
	else{
		errno = EBADF;
		return  -1;
    }
}

__attribute__ ((__noreturn__)) void _exit(int rc)
{
	while (1) {}
}

int _close(int file)
{
	errno = EBADF;
	return -1;
}

int _execve(const char *name, char *const argv[], char *const env[])
{
	errno = ENOMEM;
	return -1;
}

int _fork()
{
	errno = EAGAIN;
	return -1;
}

int _fstat(int file, struct stat *st)
{
	if (_isatty(file)==1){
		st->st_mode = S_IFCHR;
		return  0;
    }
  	else{
      	errno = EBADF;
      	return  -1;
    }
}

int _getpid ()
{
	return  1;
}

clock_t _clock (void)
{
  	clock_t timeval;

  	__asm volatile(
        "rdcntvl.w %0\n\t"
        :"=r"(timeval)
	);
	return timeval;
}

clock_t _times (struct tms * tp)
{
  	clock_t timeval = _clock();
	
  	if (tp)
    {
      	tp->tms_utime = timeval;	/* user time */
      	tp->tms_stime = 0;			/* system time */
      	tp->tms_cutime = 0;			/* user time, children */
      	tp->tms_cstime = 0;			/* system time, children */
    }

  	return timeval;
};

int _gettimeofday(struct timeval *tp, void *tzp)
{
	errno = EACCES;
	return -1;
}

int _kill(int pid, int sig)
{
	errno = EINVAL;
	return -1;
}

int _link(char *existing, char *new)
{
	errno = EMLINK;
	return -1;
}

off_t _lseek(int file, off_t ptr, int dir)
{
	if (_isatty(file)==1){
      return  0;
    }
  	else{
      	errno = EBADF;
      	return -1;
    }
}

int _open(const char *name, int flags, int mode)
{
	errno = ENOSYS;
	return -1;
}

int _stat(const char *file, struct stat *st)
{
	errno = EACCES;
	return -1;
}

int _unlink(char *name)
{
	errno = ENOENT;
	return  -1;
}

int _wait (int *status)
{
  	errno = ECHILD;
  	return -1;
}

void *_sbrk(ptrdiff_t incr)
{
  extern char _end[];
  extern char _heap_end[];
  static char *curbrk = _end;

  if ((curbrk + incr < _end) || (curbrk + incr > _heap_end))
    return NULL - 1;

  curbrk += incr;
  return curbrk - incr;
}
